# Copyright (c) 2021 Presto Labs Pte. Ltd.
# Author: fengyang
import datetime
import requests
from coin2.exchange.product_info import ProductInfoDefault
from coin2.exchange.symbology import SymbologyInfo


def week_expiration(trading_datetime):
  date = trading_datetime.date()
  if date.weekday() < 4:
    expiry_date = date + datetime.timedelta(days=(4 - date.weekday()))
  elif date.weekday() > 4:
    expiry_date = date + datetime.timedelta(days=(4 - date.weekday() + 7))
  elif trading_datetime.hour < 8:
    expiry_date = date
  elif trading_datetime.hour >= 8:
    expiry_date = date + datetime.timedelta(days=7)

  return datetime.datetime(
      year=expiry_date.year,
      month=expiry_date.month,
      day=expiry_date.day,
      hour=8)


def next_week_expiration(trading_datetime):
  return week_expiration(week_expiration(trading_datetime) + datetime.timedelta(days=1))


def last_friday_of_month(year, month):
  if month == 12:
    next_month = datetime.datetime(
        year=year + 1,
        month=1,
        day=1,
        hour=8)
  else:
    next_month = datetime.datetime(
        year=year,
        month=month + 1,
        day=1,
        hour=8)
  this_month_last_day = next_month - datetime.timedelta(days=1)
  day = this_month_last_day
  while True:
    if day.weekday() == 4:
      break
    day = day - datetime.timedelta(days=1)
  return day


def quarter_expiration(trading_datetime):
  quarter_month = ((trading_datetime.month // 4 + 1) % 5) * 3
  this_quater_friday = last_friday_of_month(trading_datetime.year, quarter_month)
  if trading_datetime > this_quater_friday:
    if quarter_month == 12:
      return last_friday_of_month(trading_datetime.year + 1, 3)
    else:
      return last_friday_of_month(trading_datetime.year, quarter_month + 3)
  else:
    return this_quater_friday


def next_quarter_expiration(trading_datetime):
  return quarter_expiration(quarter_expiration(trading_datetime) + datetime.timedelta(days=1))


def to_expire(expire_time, cycle: str):
  expire_dt = datetime.datetime.utcfromtimestamp(expire_time)
  now = datetime.datetime.utcnow()

# The check of week should come before expiration
  week = week_expiration(now)
  if cycle.find("WEEKLY") != -1:
    if (week.year == expire_dt.year and week.month == expire_dt.month and
        week.day == expire_dt.day and week.hour == expire_dt.hour):
      return "WEEK"
    next_week = next_week_expiration(now)
    if (next_week.year == expire_dt.year and next_week.month == expire_dt.month and
        next_week.day == expire_dt.day and next_week.hour == expire_dt.hour):
      return "NEXT_WEEK"

  if cycle.find("QUARTERLY") != -1:
    quarterly = quarter_expiration(now)
    if (quarterly.year == expire_dt.year and quarterly.month == expire_dt.month and
        quarterly.day == expire_dt.day and quarterly.hour == expire_dt.hour):
      return "QUARTER"
    next_quarterly = next_quarter_expiration(now)
    if (next_quarterly.year == expire_dt.year and next_quarterly.month == expire_dt.month and
        next_quarterly.day == expire_dt.day and next_quarterly.hour == expire_dt.hour):
      return "NEXT_QUARTER"

  raise ValueError("raw pi expire_time or cycle err")


def generate():
  mea = 'Futures.Gateio.v4-delivery'
  me = 'Futures.Gateio'
  default = ProductInfoDefault()
  symbol_info = SymbologyInfo()
  pis = []
  data = []
  btc_delivery = requests.get('https://api.gateio.ws/api/v4/delivery/btc/contracts').json()
  data.extend(btc_delivery)
  usdt_delivery = requests.get('https://api.gateio.ws/api/v4/delivery/usdt/contracts').json()
  data.extend(usdt_delivery)
  for info in data:
    str_list = info['underlying'].split('_')
    if len(str_list) != 2:
      continue

    expiry = to_expire(info["expire_time"], info["cycle"])
    native_symbol = info['name']
    native_base = str_list[0]
    native_quote = str_list[1]
    base = symbol_info.native_to_norm_currency(mea, native_base)
    quote = symbol_info.native_to_norm_currency(mea, native_quote)
    symbol = "%s-%s.%s" % (base, quote, expiry)

    pi = dict(
        symbol=symbol,
        base=base,
        quote=quote,
        native_symbol=native_symbol,
        native_base=native_base,
        native_quote=native_quote,
        price_ticksize=float(info['order_price_round']),
        qty_ticksize=float(info['order_size_min']),
        contract_value=float(1) if info['type'] == 'inverse' else float(info['quanto_multiplier']),
        is_inverse=(info['type'] == 'inverse'),
    )

    pi = {**pi, **default.product_info(me, symbol)}
    pis.append(pi)

  pis = sorted(pis, key=lambda x: x['symbol'])
  return pis


def gen_coin1_constants():
  data = generate()
  data = convert_coin1_constants(data)
  return data


def convert_coin1_constants(data):
  symbol_list = [info['native_symbol'] for info in data]
  quote_currency_list = [info['native_quote'] for info in data]
  base_currency_list = [info['native_base'] for info in data]
  currency_list = base_currency_list + quote_currency_list
  res = {
      'symbol_list': list(set(symbol_list)),
      'base_currency_list': list(set(base_currency_list)),
      'quote_currency_list': list(set(quote_currency_list)),
      'currency_list': list(set(currency_list)),
  }
  return res


if __name__ == "__main__":
  import json
  print(json.dumps(generate(), indent=2))
